#include "interrupt.h"
#include "ch32v_def.h"

typedef struct
{
    uint8_t irqnb;
    uint32_t irqline;
    callback_func_t callback;
} gpio_irq_conf_str;

/* Private_Defines */
#define NB_EXTI (16)

static gpio_irq_conf_str gpio_irq_conf[NB_EXTI] = {
#if defined(CH32V0xx)
    {.irqnb = EXTI0_1_IRQn, .irqline = EXTI_Line0, .callback = NULL},  // GPIO_PIN_0
    {.irqnb = EXTI0_1_IRQn, .irqline = EXTI_Line1, .callback = NULL},  // GPIO_PIN_1
    {.irqnb = EXTI2_3_IRQn, .irqline = EXTI_Line1, .callback = NULL},  // GPIO_PIN_2
    {.irqnb = EXTI2_3_IRQn, .irqline = EXTI_Line1, .callback = NULL},  // GPIO_PIN_3
    {.irqnb = EXTI4_15_IRQn, .irqline = EXTI_Line1, .callback = NULL}, // GPIO_PIN_4
    {.irqnb = EXTI4_15_IRQn, .irqline = EXTI_Line1, .callback = NULL}, // GPIO_PIN_5
    {.irqnb = EXTI4_15_IRQn, .irqline = EXTI_Line1, .callback = NULL}, // GPIO_PIN_6
    {.irqnb = EXTI4_15_IRQn, .irqline = EXTI_Line1, .callback = NULL}, // GPIO_PIN_7
    {.irqnb = EXTI4_15_IRQn, .irqline = EXTI_Line1, .callback = NULL}, // GPIO_PIN_8
    {.irqnb = EXTI4_15_IRQn, .irqline = EXTI_Line1, .callback = NULL}, // GPIO_PIN_9
    {.irqnb = EXTI4_15_IRQn, .irqline = EXTI_Line1, .callback = NULL}, // GPIO_PIN_10
    {.irqnb = EXTI4_15_IRQn, .irqline = EXTI_Line1, .callback = NULL}, // GPIO_PIN_11
    {.irqnb = EXTI4_15_IRQn, .irqline = EXTI_Line1, .callback = NULL}, // GPIO_PIN_12
    {.irqnb = EXTI4_15_IRQn, .irqline = EXTI_Line1, .callback = NULL}, // GPIO_PIN_13
    {.irqnb = EXTI4_15_IRQn, .irqline = EXTI_Line1, .callback = NULL}, // GPIO_PIN_14
    {.irqnb = EXTI4_15_IRQn, .irqline = EXTI_Line1, .callback = NULL}  // GPIO_PIN_15
#else
    {.irqnb = EXTI0_IRQn, .irqline = EXTI_Line0, .callback = NULL},
    {.irqnb = EXTI1_IRQn, .irqline = EXTI_Line1, .callback = NULL},
    {.irqnb = EXTI2_IRQn, .irqline = EXTI_Line2, .callback = NULL},
    {.irqnb = EXTI3_IRQn, .irqline = EXTI_Line3, .callback = NULL},
    {.irqnb = EXTI4_IRQn, .irqline = EXTI_Line4, .callback = NULL},
    {.irqnb = EXTI9_5_IRQn, .irqline = EXTI_Line5, .callback = NULL},
    {.irqnb = EXTI9_5_IRQn, .irqline = EXTI_Line6, .callback = NULL},
    {.irqnb = EXTI9_5_IRQn, .irqline = EXTI_Line7, .callback = NULL},
    {.irqnb = EXTI9_5_IRQn, .irqline = EXTI_Line8, .callback = NULL},
    {.irqnb = EXTI9_5_IRQn, .irqline = EXTI_Line9, .callback = NULL},
    {.irqnb = EXTI15_10_IRQn, .irqline = EXTI_Line10, .callback = NULL},
    {.irqnb = EXTI15_10_IRQn, .irqline = EXTI_Line11, .callback = NULL},
    {.irqnb = EXTI15_10_IRQn, .irqline = EXTI_Line12, .callback = NULL},
    {.irqnb = EXTI15_10_IRQn, .irqline = EXTI_Line13, .callback = NULL},
    {.irqnb = EXTI15_10_IRQn, .irqline = EXTI_Line14, .callback = NULL},
    {.irqnb = EXTI15_10_IRQn, .irqline = EXTI_Line15, .callback = NULL},
#endif
};

static inline uint8_t get_pin_id(uint16_t pin)
{
    uint8_t id = 0;
    while (pin != 0x0001)
    {
        pin = pin >> 1;
        id++;
    }
    return id;
}

static inline uint8_t get_exti_port(uint32_t port)
{
    uint8_t p = 0xFF;
    switch (port)
    {
    case GPIOA_BASE:
        p = GPIO_PortSourceGPIOA;
        break;
#ifdef GPIOB_BASE
    case GPIOB_BASE:
        p = GPIO_PortSourceGPIOB;
        break;
#endif
#ifdef GPIOC_BASE
    case GPIOC_BASE:
        p = GPIO_PortSourceGPIOC;
        break;
#endif
#ifdef GPIOD_BASE
    case GPIOD_BASE:
        p = GPIO_PortSourceGPIOD;
        break;
#endif
#ifdef GPIOE_BASE
    case GPIOE_BASE:
        p = GPIO_PortSourceGPIOE;
        break;
#endif
#ifdef GPIOF_BASE
    case GPIOF_BASE:
        p = GPIO_PortSourceGPIOF;
        break;
#endif
#ifdef GPIOG_BASE
    case GPIOG_BASE:
        p = GPIO_PortSourceGPIOG;
        break;
#endif
    default:
        break;
    }
    return p;
}

static inline uint8_t get_exti_pin(uint16_t pin)
{
    uint8_t p = 0xFF;
    switch (pin)
    {
    case GPIO_Pin_0:
        p = GPIO_PinSource0;
        break;
    case GPIO_Pin_1:
        p = GPIO_PinSource1;
        break;
    case GPIO_Pin_2:
        p = GPIO_PinSource2;
        break;
    case GPIO_Pin_3:
        p = GPIO_PinSource3;
        break;
    case GPIO_Pin_4:
        p = GPIO_PinSource4;
        break;
    case GPIO_Pin_5:
        p = GPIO_PinSource5;
        break;
    case GPIO_Pin_6:
        p = GPIO_PinSource6;
        break;
    case GPIO_Pin_7:
        p = GPIO_PinSource7;
        break;
#ifdef GPIO_Pin_8
    case GPIO_Pin_8:
        p = GPIO_PinSource8;
        break;
#endif
#ifdef GPIO_Pin_9
    case GPIO_Pin_9:
        p = GPIO_PinSource9;
        break;
#endif
#ifdef GPIO_Pin_10
    case GPIO_Pin_10:
        p = GPIO_PinSource10;
        break;
#endif
#ifdef GPIO_Pin_11
    case GPIO_Pin_11:
        p = GPIO_PinSource11;
        break;
#endif
#ifdef GPIO_Pin_12
    case GPIO_Pin_12:
        p = GPIO_PinSource12;
        break;
#endif
#ifdef GPIO_Pin_13
    case GPIO_Pin_13:
        p = GPIO_PinSource13;
        break;
#endif
#ifdef GPIO_Pin_14
    case GPIO_Pin_14:
        p = GPIO_PinSource14;
        break;
#endif
#ifdef GPIO_Pin_15
    case GPIO_Pin_15:
        p = GPIO_PinSource15;
        break;
#endif
    default:
        break;
    }
    return p;
}

void adapte_interrupt_enable(GPIO_TypeDef *port, uint16_t pin, callback_func_t callback, uint32_t mode)
{
    uint8_t id = get_pin_id(pin);

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    // GPIO_InitTypeDef GPIO_InitStructure;
    // GPIO_InitStructure.GPIO_Pin = pin;
    // GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    // GPIO_Init(port, &GPIO_InitStructure);

    gpio_irq_conf[id].callback = callback;
    GPIO_EXTILineConfig(get_exti_port((uint32_t)port), get_exti_pin(pin));
    EXTI_InitTypeDef EXTI_InitStructure;
    EXTI_InitStructure.EXTI_Line = gpio_irq_conf[id].irqline;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = (EXTITrigger_TypeDef)mode;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);

    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = gpio_irq_conf[id].irqnb;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = EXTI_IRQ_PRIO;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = EXTI_IRQ_SUBPRIO;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

void adapte_interrupt_enable(GPIO_TypeDef *port, uint16_t pin, void (*callback)(void), uint32_t mode)
{
    callback_func_t _c = callback;
    adapte_interrupt_enable(port, pin, _c, mode);
}

void adapte_interrupt_disable(GPIO_TypeDef *port, uint16_t pin)
{
    uint8_t id = get_pin_id(pin);
    gpio_irq_conf[id].callback = NULL;

    for (int i = 0; i < NB_EXTI; i++)
    {
        if (gpio_irq_conf[id].irqnb == gpio_irq_conf[i].irqnb && gpio_irq_conf[i].callback != NULL)
            return;
    }
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = gpio_irq_conf[id].irqnb;
    NVIC_InitStructure.NVIC_IRQChannelCmd = DISABLE;
    NVIC_Init(&NVIC_InitStructure);
}

void adapte_interrupt_callback(uint16_t GPIO_Pin)
{
    uint8_t irq_id = get_pin_id(GPIO_Pin);
    if (gpio_irq_conf[irq_id].callback != NULL)
    {
        gpio_irq_conf[irq_id].callback();
    }
}

#ifdef __cplusplus
extern "C"
{
#endif
    void __func_interrupt EXTI0_IRQHandler(void)
    {
        if (EXTI_GetITStatus(EXTI_Line0) == SET)
        {
            EXTI_ClearITPendingBit(EXTI_Line0);
            adapte_interrupt_callback(GPIO_Pin_0);
        }
    }

    void __func_interrupt EXTI1_IRQHandler(void)
    {
        if (EXTI_GetITStatus(EXTI_Line1) == SET)
        {
            EXTI_ClearITPendingBit(EXTI_Line1);
            adapte_interrupt_callback(GPIO_Pin_1);
        }
    }

    void __func_interrupt EXTI2_IRQHandler(void)
    {
        if (EXTI_GetITStatus(EXTI_Line2) == SET)
        {
            EXTI_ClearITPendingBit(EXTI_Line2);
            adapte_interrupt_callback(GPIO_Pin_2);
        }
    }

    void __func_interrupt EXTI3_IRQHandler(void)
    {
        if (EXTI_GetITStatus(EXTI_Line3) == SET)
        {
            EXTI_ClearITPendingBit(EXTI_Line3);
            adapte_interrupt_callback(GPIO_Pin_3);
        }
    }

    void __func_interrupt EXTI4_IRQHandler(void)
    {
        if (EXTI_GetITStatus(EXTI_Line4) == SET)
        {
            EXTI_ClearITPendingBit(EXTI_Line4);
            adapte_interrupt_callback(GPIO_Pin_4);
        }
    }

#if (((PHY_MODE == USE_MAC_RMII) || (PHY_MODE == USE_MAC_RGMII)) && !ENABLE_POLLING_TO_QUERY_PHY_LINK_STAT)
    extern void ETH_PHYLink(void);
#endif
    void __func_interrupt EXTI9_5_IRQHandler(void)
    {
        for (uint32_t pin = GPIO_Pin_5; pin <= GPIO_Pin_9; pin = pin << 1)
        {
            if (EXTI_GetITStatus(pin) == SET)
            {
                EXTI_ClearITPendingBit(pin);
                adapte_interrupt_callback(pin);
            }
        }
#if (((PHY_MODE == USE_MAC_RMII) || (PHY_MODE == USE_MAC_RGMII)) && !ENABLE_POLLING_TO_QUERY_PHY_LINK_STAT)
        ETH_PHYLink();
        EXTI_ClearITPendingBit(EXTI_Line7);
#endif
    }

    void __func_interrupt EXTI15_10_IRQHandler(void)
    {
        for (uint32_t pin = GPIO_Pin_10; pin <= GPIO_Pin_15; pin = pin << 1)
        {
            if (EXTI_GetITStatus(pin) == SET)
            {
                EXTI_ClearITPendingBit(pin);
                adapte_interrupt_callback(pin);
            }
        }
    }
#ifdef __cplusplus
}
#endif
